﻿#include <cassert>
#include <string>
#include <sstream>
#include <iostream>
#include "Position.h"
#include "Move.h"
using namespace std;

Position::Position() {
	//cout << "Position() constructor called" << endl;
	//board = new Board();
	//gameStateStack = new stack<GameState>();
}

// BE CAREFULL - MIGHT NOT WORK 
//Position::Position(Position& position)
//{
//	//cout << "Position(Position& position) constructor called" << endl;
//	//board = new Board(*(position.board));
//	board = position.board;
//	gameStateStack = position.gameStateStack;
//}

Position::Position(string strPos)
{
	//cout << "Position(string strPos) constructor called" << endl;
	//board = new Board();
	//gameStateStack = new Stack<GameState>();
	SetFromFEN(strPos);
}

void Position::SetFromFEN(string fenString)
{
	stringstream fen(fenString); // <-- use this sstream instead !!!
	fen >> std::noskipws;

	Board_Clear(board);
	gameStateStack.clear();
	Square square = SQ_A8;

	char carBoard = '#';
	while(carBoard != ' ')
	{
		fen >> carBoard ;
		switch (carBoard)
		{
		case 'K':
			Board_SetPiece(board, W_KING, square);
			square++;
			break;
		case 'Q':
			Board_SetPiece(board, W_QUEEN, square);
			square++;
			break;
		case 'R':
			Board_SetPiece(board,W_ROOK, square);
			square++;
			break;
		case 'N':
			Board_SetPiece(board,W_KNIGHT, square);
			square++;
			break;
		case 'B':
			Board_SetPiece(board,W_BISHOP, square);
			square++;;
			break;
		case 'P':
			Board_SetPiece(board,W_PAWN, square);
			square++;
			break;
		case 'k':
			Board_SetPiece(board,B_KING, square);
			square++;
			break;
		case 'q':
			Board_SetPiece(board,B_QUEEN, square);
			square++;
			break;
		case 'r':
			Board_SetPiece(board,B_ROOK, square);
			square++;
			break;
		case 'n':
			Board_SetPiece(board,B_KNIGHT, square);
			square++;
			break;
		case 'b':
			Board_SetPiece(board,B_BISHOP, square);
			square++;
			break;
		case 'p':
			Board_SetPiece(board,B_PAWN, square);
			square++;
			break;
		case '/':
			square -= (Square)16;
			break;
		case '1':
			square += (Square)1;
			break;
		case '2':
			square += (Square)2;
			break;
		case '3':
			square += (Square)3;
			break;
		case '4':
			square += (Square)4;
			break;
		case '5':
			square += (Square)5;
			break;
		case '6':
			square += (Square)6;
			break;
		case '7':
			square += (Square)7;
			break;
		case '8':
			square += (Square)8;
			break;
		default:
			//goto endBoard;
			break;
		}
	}

	char ply = carBoard;
	fen >> ply;
	//ply = fenString[index++];
	switch (ply)
	{
	case 'w':
		gameState.turn = WHITE;
		break;
	case 'b':
		gameState.turn = BLACK;
		break;
	default:
		break;
	}
	fen >> ply;

	gameState.whiteCanCastleKingSide = false;
	gameState.whiteCanCastleQueenSide = false;
	gameState.blackCanCastleKingSide = false;
	gameState.blackCanCastleQueenSide = false;
	char carCastle = '#';
	while(carCastle != ' ')
	{
		fen >> carCastle;
		switch (carCastle)
		{
		case 'K':
			gameState.whiteCanCastleKingSide = true;
			break;
		case 'Q':
			gameState.whiteCanCastleQueenSide = true;
			break;
		case 'k':
			gameState.blackCanCastleKingSide = true;
			break;
		case 'q':
			gameState.blackCanCastleQueenSide = true;
			break;
		default:
			break;
		}
	}


	char enPassant1, enPassant2;
	fen >> enPassant1;
	if (enPassant1 != '-')
	{
		fen >> enPassant2;
		gameState.lastEnPassant = make_square((File)(enPassant1- 'a'),(Rank)(enPassant2 - '1'));
	} else {
		gameState.lastEnPassant = SQ_NONE;
	}

	fen >> std::skipws;

	fen >> gameState.halfMoveClock;
	fen >> gameState.fullMoveNumber;

	
	//////DEBUG PART
	//Board_DebugDisplay(this->board);
	//cout << "initial fen: " << fenString << endl;
	//cout << "result fen:  " << this->GetFEN() << endl;
}





// Perform the move, augment gameStack and change turn
void Position::Play(Move m)
{
	assert(this->gameStateStack.size() >= 0);
	//assert((m.cPiece != W_KING) && (m.cPiece != B_KING));
	assert(from_sq(m) != to_sq(m));

	Piece piece = Board_GetPiece(board, from_sq(m));
	Piece cPiece = Board_GetPiece(board, to_sq(m));

	// DEBUG CODE ******************
	if(piece == NO_PIECE){
		cout << "TRYING TO PLAY: " << move_to_string(m) << endl;
		this->DebugDisplay();
		assert(piece != NO_PIECE);
	}
	if(color_of(piece) != this->GetTurn()){
		cout << "TRYING TO PLAY OPPONENT'S PIECE !" << endl;
		this->DebugDisplay();
		assert(color_of(piece) == this->GetTurn());
	}
	//*******************************

	// push the previous gamestate to the stack 
	gameStateStack.push_back(gameState);

	gameState.playedPiece = piece;
	gameState.capturedPiece = cPiece;

	// En-passant extra-move (remove opponant's pawn)
	if (cPiece == NO_PIECE && file_of(from_sq(m)) != file_of(to_sq(m)))
	{
		if (piece == W_PAWN) Board_Clear(board, to_sq(m) + DELTA_S);
		if (piece == B_PAWN) Board_Clear(board, to_sq(m) + DELTA_N);
	}

	// Castleling extra-move (Rook)
	if (piece == W_KING && from_sq(m) == SQ_E1 && to_sq(m) == SQ_G1) 
		Board_DoMove(board, make_move(SQ_H1, SQ_F1));
	if (piece == W_KING && from_sq(m) == SQ_E1 && to_sq(m) == SQ_C1) 
		Board_DoMove(board, make_move(SQ_A1, SQ_D1));
	if (piece == B_KING && from_sq(m) == SQ_E8 && to_sq(m) == SQ_G8) 
		Board_DoMove(board, make_move(SQ_H8, SQ_F8));
	if (piece == B_KING && from_sq(m) == SQ_E8 && to_sq(m) == SQ_C8) 
		Board_DoMove(board, make_move(SQ_A8, SQ_D8));

	// Castleling - canceller
	// TODO: find a clean and quick way to update this status
	if (piece == W_KING)
	{
		gameState.whiteCanCastleKingSide = false;
		gameState.whiteCanCastleQueenSide = false;
	}
	else if (piece == B_KING)
	{
		gameState.blackCanCastleKingSide = false;
		gameState.blackCanCastleQueenSide = false;
	}
	else if (piece == W_ROOK && from_sq(m) == SQ_A1)
	{
		gameState.whiteCanCastleQueenSide = false;
	}
	else if (piece == W_ROOK && from_sq(m) == SQ_H1)
	{
		gameState.whiteCanCastleKingSide = false;
	}
	else if (piece == B_ROOK && from_sq(m) == SQ_A8)
	{
		gameState.blackCanCastleQueenSide = false;
	}
	else if (piece == B_ROOK && from_sq(m) == SQ_H8)
	{
		gameState.blackCanCastleKingSide = false;
	}

	// keeping en-passant last move
	if ((piece == W_PAWN && (rank_of(to_sq(m)) - rank_of(from_sq(m))) == 2)
		|| (piece == B_PAWN && (rank_of(to_sq(m)) - rank_of(from_sq(m))) == 2))
	{
		gameState.lastEnPassant = (from_sq(m)<32)?(from_sq(m)+DELTA_N):(from_sq(m)+DELTA_S);
	}
	else
	{
		gameState.lastEnPassant = SQ_NONE;
	}


	// standard move
	Board_DoMove(board, m);


	// promotion
	int turn = gameState.turn;
	if(((piece == W_PAWN) && (rank_of(to_sq(m)) == RANK_8 ))
		|| ((piece == B_PAWN) && (rank_of(to_sq(m)) == RANK_1)))
		if (promotion_type(m) != NO_PIECE_TYPE)
		{
			switch (promotion_type(m))
			{
			case QUEEN:
				if (turn == WHITE) this->SetPiece(W_QUEEN, to_sq(m));
				else this->SetPiece(B_QUEEN, to_sq(m));
				break;
			case ROOK:
				if (turn == WHITE) this->SetPiece(W_ROOK, to_sq(m));
				else this->SetPiece(B_ROOK, to_sq(m));
				break;
			case BISHOP:
				if (turn == WHITE) this->SetPiece(W_BISHOP, to_sq(m));
				else this->SetPiece(B_BISHOP, to_sq(m));
				break;
			case KNIGHT:
				if (turn == WHITE) this->SetPiece(W_KNIGHT, to_sq(m));
				else this->SetPiece(B_KNIGHT, to_sq(m));
				break;
			}

		}


		gameState.turn = (BLACK-gameState.turn);
		// 50 plies rule
		if (cPiece != NO_PIECE
			|| piece == W_PAWN
			|| piece == B_PAWN)
			gameState.halfMoveClock = 0;
		else
			//gameState.halfMoveClock = gameStateStack.back().halfMoveClock + 1;
			gameState.halfMoveClock++;
		// full move clock (+1 only after black ply)
		/*
		if (turn==WHITE)
			gameState.fullMoveNumber = gameStateStack.back().fullMoveNumber;
		else
			gameState.fullMoveNumber = gameStateStack.back().fullMoveNumber + 1;
			*/
		if(turn == BLACK)
			gameState.fullMoveNumber++;

		// NOTE: time consuming - no need
		// store pseudFen for 3-times repetition rule
		//gameState.fen = GetFEN();

		//// push the new state in the gameStateStack
		//gameStateStack.push_back(newGameState);

		///////// UGGLY DEBUG CODE TO CATCH ILLEGAL MOVES PLAYED BEFORE
		//if((cPiece == W_KING) ||
		//	(cPiece == B_KING)){
		//	Position p;
		//	cout << "WARNING: captured King!!! an illegal move was allowed the move just before" << endl;
		//	cout << "MOVE SEQUENCE" << endl;

		//	for(int i=0;i<4;i++){
		//		GameState gs = gameStateStack.back();
		//		gameStateStack.pop_back();

		//		cout << "Position n-" << i << endl;
		//		p.SetFromFEN(gs.fen);
		//		p.DebugDisplay();
		//		//cout << gs.fen << endl;
		//	}
		//}
		//////////////////////
}

void Position::Unplay(Move m)
{
	assert(this->gameStateStack.size() >= 1);
	// Unplay - base part
	Piece piece = gameState.playedPiece;
	Piece cPiece = gameState.capturedPiece;

	//cout << "undo move" << endl;
	//DebugDisplay();

	Board_UndoMove(board, m, piece,cPiece);

	// recover previous gamestate
	gameState = gameStateStack.back();
	//memcpy(&gameState, &gameStateStack.back(), sizeof(GameState));

	// Pop the state to return to the n-1 game state
	gameStateStack.pop_back();

	// recover pre-casteling position
	if (piece == W_KING) {
		if(from_sq(m) == SQ_E1)
		{
			if(to_sq(m) == SQ_G1) 
				Board_DoMove(board, make_move(SQ_F1, SQ_H1));
			if (to_sq(m) == SQ_C1) 
				Board_DoMove(board, make_move(SQ_D1, SQ_A1));
		}
	}
	else if (piece == B_KING) {
		if(from_sq(m) == SQ_E8)
		{
			if(to_sq(m) == SQ_G8) 
				Board_DoMove(board, make_move(SQ_F8, SQ_H8));
			if (to_sq(m) == SQ_C8) 
				Board_DoMove(board, make_move(SQ_D8,SQ_A8));
		}
	}

	// recover en-passant captured pawns
	// TODO: improve this code
	if (cPiece == NO_PIECE) {
		if(file_of(from_sq(m)) != file_of(to_sq(m)))
		{
			if (piece == W_PAWN) 
				Board_SetPiece(board, B_PAWN, to_sq(m) + DELTA_S);
			if (piece == B_PAWN) 
				Board_SetPiece(board, W_PAWN, to_sq(m) + DELTA_N);
		}
	}

}

void Position::SetPiece(Piece piece, Square square)
{
	// TODO : improve this code
	if (piece == NO_PIECE)
	{
		Board_Clear(board, square);
	}
	else
	{
		Board_SetPiece(board, piece, square);
	}
}

void Position::SetStartPosition()
{
	this->SetFromFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
}

//private static char[] fenPieceCodes = { '-', '-', 'P', 'N', 'B', 'R', 'Q', 'K', 'p', 'n', 'b', 'r', 'q', 'k' };
//const char fenPieceCodes[] = { '-', '-', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r', 'Q', 'q', 'K', 'k' };
//private string strPos;

string Position::GetFEN()
{
	//StringBuilder result = new StringBuilder();
	stringstream result;
	int emptySquares = 0;
	for (Square i = SQ_A8; i >= SQ_A1; i += DELTA_S)
	{
		for (Square j = (Square)0; j < 8; j++)
		{

			Piece p = Board_GetPiece(board, i + j);
			if (p == NO_PIECE)
			{
				emptySquares++;
			}
			else
			{
				if (emptySquares > 0)
				{
					//result.Append(emptySquares);
					result << emptySquares;
					emptySquares = 0;
				}
				//result.Append(fenPieceCodes[(int)p]);
				//  NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
				//W_PAWN = 1, W_KNIGHT =  2, W_BISHOP =  3, W_ROOK =  4, W_QUEEN =  5, W_KING =  6,
				//B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
				result << " PNBRQK  pnbrqk  "[(int)p];

			}
		}
		if (emptySquares > 0)
		{
			//result.Append(emptySquares);
			result << emptySquares;
			emptySquares = 0;
		}
		if (i != 0)
			//result.Append('/');
			result << '/';
	}

	//result.Append(' ');
	result << ' ';

	//result.Append(GetTurn()==WHITE ? 'w' : 'b');
	result << (GetTurn()==0 ? 'w' : 'b'); //WHITE==0

	//result.Append(' ');
	result << ' ';

	if (gameState.whiteCanCastleKingSide)
		//result.Append('K');
		result << 'K';
	if (gameState.whiteCanCastleQueenSide)
		//result.Append('Q');
		result << 'Q';
	if (gameState.blackCanCastleKingSide)
		//result.Append('k');
		result << 'k';
	if (gameState.blackCanCastleQueenSide)
		//result.Append('q');
		result << 'q';

	if (!gameState.whiteCanCastleKingSide
		&& !gameState.whiteCanCastleQueenSide
		&& !gameState.blackCanCastleKingSide
		&& !gameState.blackCanCastleQueenSide)
		//result.Append('-');
		result << '-';

	//result.Append(' ');
	result << ' ';

	if (gameState.lastEnPassant != SQ_NONE)
		//result.Append(gs.lastEnPassant.ToString());
		result << square_to_string(gameState.lastEnPassant);
	else
		//result.Append('-');
		result << '-';

	//result.Append(' ');
	result << ' ';

	//result.Append(gs.halfMoveClock);
	result << gameState.halfMoveClock;

	//result.Append(' ');
	result << ' ';

	//result.Append(gs.fullMoveNumber);
	result << gameState.fullMoveNumber;

	return result.str();

}

///*
//* Unique key generator for positions
//*/
//string Position::GetPseudoFEN()
//{
//	//StringBuilder result = new StringBuilder();
//	stringstream result;
//	int emptySquares = 0;
//	for (Square i = SQ_A1; i <= SQ_H8; i++)
//	{
//		Piece p = Board_GetPiece(board, i);
//		if (p == NO_PIECE)
//		{
//			emptySquares++;
//		}
//		else
//		{
//			if (emptySquares > 0)
//			{
//				//result.Append(emptySquares);
//				result << emptySquares;
//				emptySquares = 0;
//			}
//			result << " PNBRQK  pnbrqk  "[(int)p];
//		}
//	}
//	GameState gs = this->gameStateStack.back();
//	result << gs.intTurn;
//	result << gs.whiteCanCastleQueenSide ? '1' : '0';
//	result << gs.whiteCanCastleKingSide ? '1' : '0';
//	result << gs.blackCanCastleQueenSide ? '1' : '0';
//	result << gs.blackCanCastleKingSide ? '1' : '0';
//
//	// TODO: see if this line is not too much restrictive
//	// depth (= limit TT application to equal depth)
//	//result.Append(gameStateStack.size());
//
//	return result.str();
//}





// for test only !!!
bool Position::IsEqual(Position& pos)
{
	return Board_IsEqual(board, pos.board);//		&& IsEqual(gameStateStack,pos.gameStateStack);
}


/*
// for test only !!!
bool Position::IsEqual(vector<GameState> s1, vector<GameState> s2)
{
vector<GameState> st1 = new vector<GameState>(s1); 
vector<GameState> st2 = new vector<GameState>(s2); 

while(st1.size()>0 && st2.size()>0)
{
GameState g1 = st1.back();
GameState g2 = st2.back();
if (!g1.IsEqual(g2)) return false;
st1.pop_back();
st2.pop_back();
}

return (st1.size() == 0) && (st2.size() == 0);
}
*/

int Position::Evaluate()
{
	// TODO: be carefull on the coefficients (new enum Piece for bitboards) 
	return Evaluate(GetTurn());
}

int Position::Evaluate(Color color)
{
	if(color == WHITE)
		return Board_Evaluate(board);
	return -Board_Evaluate(board);
}


int Position::GetPlayedMoveNumber()
{
	return gameStateStack.size();
}


//int Position::GetMovesWithoutCaptureOrPawnPush()
//{
//	return gameStateStack.back().halfMoveClock;
//}

//// Not mandatory
//bool Position::IsThreeTimesRepetitionDraw()
//{
//	// TODO: implement this part
//	/*
//	assert(gameStateStack.size() >= 6);
//	GameState ags[] = gameStateStack.ToArray<GameState>();
//
//	int top = ags.Length-1;
//
//	return (ags[top].pseudoFen == ags[top - 2].pseudoFen)
//	&& (ags[top - 2].pseudoFen == ags[top - 4].pseudoFen);
//	*/
//	return false;
//}

void Position::DebugDisplay()
{
	if(this->GetTurn() == WHITE)
		cout << "White to move" << endl;
	else
		cout << "Black to move" << endl;

	cout << "Castle rights: [";

	if(gameState.whiteCanCastleKingSide)
		cout << 'K';
	if(gameState.whiteCanCastleQueenSide)
		cout << 'Q';
	if(gameState.blackCanCastleKingSide)
		cout << 'k';
	if(gameState.blackCanCastleQueenSide)
		cout << 'q';
	cout << ']' << endl;

	Board_DebugDisplay(board);
}

bool Position::IsCheck()
{
	return Board_IsCheck(board, this->GetTurn());
}

bool Position::IsCheck(Color side)
{
	return Board_IsCheck(board, side);
}

bool Position::IsCheck(Color side, Square square)
{
	return Board_IsCheck(board, square, BLACK-side);
}

